package intopo.game.impl.sym2x2;

import intopo.game.Game;

import java.io.PrintStream;
import java.util.Scanner;
import util.xml.*;

import org.w3c.dom.Element;
import java.util.Vector;

/**
 * A class that represents 2-player 2x2 symmetric game.
 *
 * <p> This class provides methods to construct several well known 2x2
 * symmetric games.

 * <p> Its corresponding strategy, represented by class {@code
 * Symmetric2x2Strategy}, contains a method the returns {@code true} if the
 * strategy plays the first action (index 0). Interpretation of this action
 * depends on the particular instance of the game class.

 * <p> In the payoff matrix, element {@code payoffMatrix[x][y]} represents
 * the payoff obtained by the player that played action {@code x} while its
 * partner played action {@code x}.
 *
 * @author <a href="mailto:plsm@ua.pt">Pedro Lopes da Silva Mariano</a>

 * @version 1.0
 */
public class Symmetric2x2Game
	implements Game<Symmetric2x2Strategy>
{
	/**
	 *
	 */
	static String CONSTRUCTOR_TAG = "Symmetric2x2Game";
	/**
	 * The 2x2 payoff matrix of this game.
	 */
	final private float payoffMatrix[][];
	final private float lowestPayoff;
	final private float highestPayoff;
	final private float paretoPayoff;

	/**
	 * Construct a new instance with the given payoffs.

	 * <p> The parameters of this constructor represent the payoffs. The
	 * following table shows how these parameters should be interpreted from
	 * the point of view of Row Player's payoff:

	 * <table><tr><td>{@code payoff00}</td><td>{@code payoff01}</td></tr>
	 * <tr><td>{@code payoff10}</td><td>{@code payoff11}</td></tr></table>

	 * @param payoff00 Top row, left column payoff.
	 * @param payoff01 Top row, right column payoff.
	 * @param payoff10 Bottom row, left column payoff.
	 * @param payoff11 Bottom row, right column payoff.
	 */
	public Symmetric2x2Game (float payoff00, float payoff01, float payoff10, float payoff11)
	{
 		payoffMatrix = new float[2][2];
		payoffMatrix [0][0] = payoff00;
		payoffMatrix [0][1] = payoff01;
		payoffMatrix [1][0] = payoff10;
		payoffMatrix [1][1] = payoff11;
		lowestPayoff = Math.min (Math.min (payoff00, payoff01), Math.min (payoff10, payoff11));
		highestPayoff = Math.max (Math.max (payoff00, payoff01), Math.max (payoff10, payoff11));
		paretoPayoff = Math.max (Math.max (payoff00, (payoff01 + payoff10) / 2), payoff11);
	}
	/**
	 *
	 * @param scanner
	 */
	public Symmetric2x2Game (Scanner scanner)
	{
		this (scanner.nextFloat (), scanner.nextFloat (), scanner.nextFloat (), scanner.nextFloat ());
	}
	/**
	 *
	 * @param element
	 * @throws util.xml.XMLParseException
	 */
	public Symmetric2x2Game (Element element)
		throws XMLParseException
	{
		this
			(AttributeParser.getDouble ().parse (element, "payoff00").floatValue (),
			AttributeParser.getDouble ().parse (element, "payoff01").floatValue (),
			AttributeParser.getDouble ().parse (element, "payoff10").floatValue (),
			 AttributeParser.getDouble ().parse (element, "payoff11").floatValue ());
// 		AttributeParser<Double> apProb = new AttributeParser<Double>
// 			(new CheckExistenceAttribute<Double> ());
// 		payoffMatrix = new float[2][2];
// 		payoffMatrix [0][0] = apProb.parse (element, "payoff00").floatValue ();
// 		payoffMatrix [0][1] = apProb.parse (element, "payoff01").floatValue ();
// 		payoffMatrix [1][0] = apProb.parse (element, "payoff10").floatValue ();
// 		payoffMatrix [1][1] = apProb.parse (element, "payoff11").floatValue ();
// 		lowestPayoff = Math.min (
// 			Math.min (payoffMatrix [0][0], payoffMatrix [0][1]),
// 			Math.min (payoffMatrix [1][0], payoffMatrix [1][1]));
// 		highestPayoff = Math.max (
// 			Math.max (payoffMatrix [0][0], payoffMatrix [0][1]),
// 			Math.max (payoffMatrix [1][0], payoffMatrix [1][1]));
// 		paretoPayoff = Math.max (Math.max (payoffMatrix [0][0],
// 			(payoffMatrix [0][1] + payoffMatrix [1][0]) / 2), payoffMatrix [1][1]);
	}

	public int numberPlayers ()
	{
		return 2;
	}

	public void play (Strategies<Symmetric2x2Strategy> players)
		throws IllegalArgumentException
	{
		final int action[] = new int [2];
		for (int i = 0; i < 2; i++) {
			Symmetric2x2Strategy player = players.getStrategy (i);
			if (player.firstAction ())
				action [i] = 0;
			else
				action [i] = 1;
		}
		for (int i = 0; i < 2; i++) {
			players.setPayoff (i, payoffMatrix [action [i]][action [1 - i]]);
		}
	}

	public float getPayoff (int i, int j)
	{
		return this.payoffMatrix [i][j];
	}
	
	public float lowestPayoff ()
	{
		return lowestPayoff;
	}

	public float highestPayoff ()
	{
		return highestPayoff;
	}

	public float paretoPayoff ()
	{
		return paretoPayoff;
	}

	/**
	 * Construct an instance that represents a Prisoner's Dilemma
	 * game. Action index 0 corresponds to <i>cooperate</i>. The following
	 * table shows the correspondence between element of the payoff matrix
	 * {@code payoffMatrix} and the game payoffs:

	 * <table> <tr><td>{@code payoffMatrix[0][0]}</td><td>cooperate</td></tr>

	 * <tr><td>{@code payoffMatrix[0][1]}</td><td>suckers</td></tr>

	 * <tr><td>{@code payoffMatrix[1][0]}</td><td>temptation</td></tr>

	 * <tr><td>{@code payoffMatrix[1][1]}</td><td>penalty</td></tr>
	 * </table>

	 * @param temptation Temptation payoff (player defected while partner cooperated).
	 * @param cooperate Cooperation payoff (both players cooperated).
	 * @param penalty Penalty payoff (both players defected).
	 * @param suckers Suckers payoff (player cooperated while partner defected).
	 */
	static public Symmetric2x2Game createPrisonerDilemma (float temptation, float cooperate, float penalty, float suckers)
	{
		assert temptation > cooperate && cooperate > penalty && penalty > suckers && 2 * cooperate > temptation + penalty;
		return new Symmetric2x2Game (cooperate, suckers, temptation, penalty);
	}
	/**
	 * 
	 * @param ps
	 */
	@Override
	public void write (PrintStream ps)
	{
		ps.print (CONSTRUCTOR_TAG);
		ps.print (' ');
		ps.print (this.payoffMatrix [0][0]);
		ps.print (' ');
		ps.print (this.payoffMatrix [0][1]);
		ps.print (' ');
		ps.print (this.payoffMatrix [1][0]);
		ps.print (' ');
		ps.print (this.payoffMatrix [1][1]);
	}
}

